home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xmessage / makeform.c < prev    next >
C/C++ Source or Header  |  1995-06-22  |  15KB  |  549 lines

  1. /*
  2.  * xmessage - utility for querying users
  3.  *
  4.  * Copyright 1988,1991 Massachusetts Institute of Technology
  5.  *
  6.  * Permission to use, copy, modify, and distribute this software and its
  7.  * documentation for any purpose and without fee is hereby granted, provided
  8.  * that the above copyright notice appear in all copies and that both that
  9.  * copyright notice and this permission notice appear in supporting
  10.  * documentation, and that the name of M.I.T. not be used in advertising or
  11.  * publicity pertaining to distribution of the software without specific,
  12.  * written prior permission.  M.I.T. makes no representations about the
  13.  * suitability of this software for any purpose.  It is provided "as is"
  14.  * without express or implied warranty.
  15.  */
  16.  
  17. #include <X11/Intrinsic.h>
  18. #include <X11/StringDefs.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <sys/types.h>
  22.  
  23. #include <X11/Xaw/AsciiText.h>
  24. #include <X11/Xaw/Command.h>
  25. #include <X11/Xaw/Form.h>
  26.  
  27. #define    SCROLLBAR_WIDTH    14
  28. #define    SCROLLBAR_HEIGHT 14
  29.  
  30. extern char *malloc();
  31. extern char *ProgramName;
  32. extern Widget default_button_widget;
  33. extern char *copy_stdin();
  34.  
  35. typedef struct _ButtonRecord {
  36.     char *name;            /* button name */
  37.     char *filename;        /* file to unlink on exit, or NULL */
  38.     int exitstatus;        /* exit status for this button */
  39.     Boolean print_value;    /* print button name on exit? */
  40.     Widget widget;        /* button Command widget */
  41. } ButtonRecord;
  42.  
  43.  
  44. /*
  45.  * event handler to warp the pointer to the default button when the button
  46.  * is exposed.
  47.  */
  48.  
  49. static XtEventHandler warp_to_button (w, closure, event, continue_to_dispatch)
  50.     Widget w;            /* widget that was exposed */
  51.     XtPointer closure;        /* ButtonRecord to warp pointer to */
  52.     XEvent *event;        /* event */
  53.     Boolean *continue_to_dispatch;    /* ignored */
  54. {
  55.     ButtonRecord *br;
  56.     Arg args[3];        /* argument list */
  57.     Cardinal num_args;
  58.     Dimension width, height, b_width;
  59.     int x, y;
  60.  
  61.     br = (ButtonRecord *) closure;
  62.  
  63.     num_args = 0;
  64.     (void) XtSetArg(args[num_args], XtNwidth, &width); num_args++;
  65.     (void) XtSetArg(args[num_args], XtNheight, &height); num_args++;
  66.     (void) XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++;
  67.     (void) XtGetValues(br->widget, args, num_args);
  68.  
  69.     width += 2 * b_width;
  70.     height += 2 * b_width;
  71.     x = width / 2;
  72.     y = height / 2;
  73.  
  74.     XWarpPointer (XtDisplay(br->widget), None, XtWindow(br->widget),
  75.           0, 0, 0, 0, x, y);
  76.  
  77.     XtRemoveEventHandler(w, ExposureMask, False, (XtEventHandler)warp_to_button,
  78.                 (XtPointer) br);
  79. }
  80.  
  81.  
  82. /*
  83.  * compute width and height of message in terms of columns and lines
  84.  */
  85. static void get_message_dimensions (message, width, height)
  86.     char *message;            /* message string to be displayed */
  87.     int *width;                /* width needed to display string */
  88.     int *height;            /* height needed to display string */
  89. {
  90.     int lines, cols, maxcols;
  91.     char *cp;
  92.  
  93.     *width = 0;
  94.     *height = 0;
  95.  
  96.     lines = 0;
  97.     cols = 0;
  98.     maxcols = 0;
  99.  
  100.     for (cp = message; *cp; cp++) {
  101.     switch (*cp)
  102.     {
  103.     case '\n':
  104.         lines++;
  105.         if (cols > maxcols) maxcols = cols;
  106.         cols = 0;
  107.         break;
  108.  
  109.     case '\t':
  110.         cols = ((cols >> 3) + 1) << 3;
  111.  
  112.     default:
  113.         cols++;
  114.         break;
  115.     }
  116.     }
  117.  
  118.     if (cols > 0) {
  119.     lines++;
  120.     if (cols > maxcols) maxcols = cols;
  121.     }
  122.  
  123.     *width = maxcols;
  124.     *height = lines;
  125. }
  126.  
  127.  
  128. /*
  129.  * compute width and height of file contents in terms of columns and lines
  130.  */
  131. static void get_file_dimensions (filename, width, height)
  132.     char *filename;            /* file to be displayed */
  133.     int *width;                /* width needed to display string */
  134.     int *height;            /* height needed to display string */
  135. {
  136.     int lines, cols, maxcols;
  137.     int c;
  138.     FILE *fp;
  139.  
  140.     *width = 0;
  141.     *height = 0;
  142.  
  143.     fp = fopen (filename, "r");
  144.     if (!fp) return;
  145.  
  146.     lines = 0;
  147.     cols = 0;
  148.     maxcols = 0;
  149.  
  150.     while ((c = getc(fp)) != EOF) {
  151.     switch (c)
  152.     {
  153.     case '\n':
  154.         lines++;
  155.         if (cols > maxcols) 
  156.         maxcols = cols;
  157.         cols = 0;
  158.         break;
  159.  
  160.     case '\t':
  161.         cols = ((cols >> 3) + 1) << 3;
  162.  
  163.     default:
  164.         cols++;
  165.         break;
  166.     }
  167.     }
  168.  
  169.     if (cols > 0) {
  170.     lines++;
  171.     if (cols > maxcols) maxcols = cols;
  172.     }
  173.  
  174.     (void) fclose (fp);
  175.     *width = maxcols;
  176.     *height = lines;
  177. }
  178.  
  179.  
  180. /*
  181.  * strip quotes from button names
  182.  */
  183. static void unquote_pairs (br, n)
  184.     register ButtonRecord *br;
  185.     int n;
  186. {
  187.     int i;
  188.  
  189.     for (i = 0; i < n; i++) {
  190.     register char *dst, *src;
  191.     register int quoted = 0;
  192.  
  193.     for (src = dst = br->name; *src; src++) {
  194.         if (quoted) {
  195.         *dst++ = *src;
  196.         quoted = 0;
  197.         } else if (src[0] == '\\') {
  198.         quoted = 1;
  199.         } else {
  200.         *dst++ = *src;
  201.         }
  202.     }
  203.     *dst = '\0';
  204.     }
  205.     return;
  206. }
  207.  
  208.  
  209. /*
  210.  * parses string of form "yes:1,no:2, ..."
  211.  * sets brptr to point to parsed table
  212.  * returns 0 if successful, -1 if not
  213.  */
  214.  
  215. static int parse_name_and_exit_code_list (buttonlist, brptr)
  216.     char *buttonlist;
  217.     ButtonRecord **brptr;
  218. {
  219.     register char *cp;
  220.     int shouldfind = 0, npairs = 0;
  221.     int default_exitcode = 100;
  222.     int quoted = 0;
  223.     ButtonRecord *br;
  224.     int len;
  225.     char *copy;
  226.  
  227.     if (!buttonlist) return 0;
  228.  
  229.     /*
  230.      * Figure out how many matches we will find so that we can preallocate
  231.      * space for button structures.  If you add stripping of white space,
  232.      * make sure that you update this as well as the walking algorithm below.
  233.      */
  234.     if (buttonlist[0]) shouldfind++;
  235.     for (cp = buttonlist; *cp; cp++) {
  236.     if (quoted == 1) quoted = 0;
  237.     else if (*cp == '\\') quoted = 1;
  238.     else if (*cp == ',') shouldfind++;
  239.     }
  240.     len = (cp - buttonlist);
  241.  
  242.     /*
  243.      * allocate space for button record
  244.      */
  245.     br = (ButtonRecord *) malloc (sizeof(ButtonRecord) * shouldfind);
  246.     if (!br) return -1;
  247.  
  248.     cp = malloc (len + 1);
  249.     if (!cp) {
  250.     (void) free ((char *) br);
  251.     return -1;
  252.     }
  253.     copy = cp;
  254.     strcpy (copy, buttonlist);
  255.  
  256.  
  257.     /*
  258.      * walk down list separating into name:exitcode pairs
  259.      */
  260.     while (*cp) {
  261.     char *start, *colon, *comma;
  262.     int exitcode;
  263.  
  264.     start = cp;
  265.     colon = comma = NULL;
  266.     exitcode = ++default_exitcode;
  267.     quoted = 0;
  268.  
  269.     /* find the next name and exit code */
  270.     for (; *cp; cp++) {
  271.         if (quoted) quoted = 0;
  272.         else if (*cp == '\\') quoted = 1;
  273.         else if (*cp == ':') colon = cp;
  274.         else if (*cp == ',') {
  275.         comma = cp;
  276.         break;
  277.         }
  278.     }
  279.  
  280.     /*
  281.      * If comma is NULL then we are at the end of the string.  If colon
  282.      * is NULL, then there was no exit code given, so default to zero.
  283.      */
  284.  
  285.     if (comma) *comma = '\0';
  286.  
  287.     if (colon) {
  288.         exitcode = atoi (colon+1);
  289.         *colon = '\0';
  290.     }
  291.  
  292.     /*
  293.      * make sure that we aren't about to stomp on memory
  294.      */
  295.     if (npairs >= shouldfind) {
  296.         fprintf (stderr,
  297.              "%s:  internal error, found extra pairs (should be %d)\n",
  298.              ProgramName, shouldfind);
  299.         (void) free ((char *) br);
  300.         (void) free (copy);
  301.         return -1;
  302.     }
  303.  
  304.     /*
  305.      * got it!  start and exitcode contain the right values
  306.      */
  307.     br[npairs].name = start;
  308.     br[npairs].exitstatus = exitcode;
  309.     npairs++;
  310.  
  311.     if (comma) cp++;
  312.     }
  313.  
  314.  
  315.     if (npairs != shouldfind) {
  316.     fprintf (stderr, "%s:  internal error found %d instead of %d pairs\n",
  317.          ProgramName, npairs, shouldfind);
  318.     (void) free ((char *) br);
  319.     (void) free (copy);
  320.     return -1;
  321.     }
  322.  
  323.     /*
  324.      * now, strip any quoted characters
  325.      */
  326.     unquote_pairs (br, npairs);
  327.     *brptr = br;
  328.     return npairs;
  329. }
  330.  
  331. /*
  332.  * callback to handle button being pressed
  333.  */
  334.  
  335. /* ARGSUSED */
  336. static void handle_button (w, closure, client_data)
  337.     Widget w;
  338.     XtPointer closure;
  339.     XtPointer client_data;
  340. {
  341.     ButtonRecord *br = (ButtonRecord *) closure;
  342.  
  343.     if (br->print_value) printf("%s\n", br->name);
  344.     if (br->filename) (void) unlink(br->filename);
  345.     exit (br->exitstatus);
  346. }
  347.  
  348. Widget make_queryform(parent, msgstr, filename, button_list,
  349.               print_value, default_button, warp_button,
  350.               columns, min_columns, max_columns,
  351.               lines, min_lines, max_lines)
  352.     Widget parent;            /* into whom widget should be placed */
  353.     char *msgstr;            /* message string  or NULL */
  354.     char *filename;            /* file name or NULL */
  355.     char *button_list;            /* list of button title:status */
  356.     Boolean print_value;        /* print button string on stdout? */
  357.     char *default_button;        /* button activated by Return */
  358.     char *warp_button;            /* button to warp cursor to */
  359.     int columns;            /* requested size of message box */
  360.     int min_columns;            /* min size of message box */
  361.     int max_columns;            /* max size of message box */
  362.     int lines;                /* requested size of message box */
  363.     int min_lines;            /* min size of message box */
  364.     int max_lines;            /* max size of message box */
  365. {
  366.     ButtonRecord *br;
  367.     int npairs, i;
  368.     Widget form, text, prev;
  369.     Arg args[20];
  370.     Cardinal n, thisn;
  371.     int width, height;
  372.     int msg_lines, msg_columns;
  373.     int font_width, font_height;
  374.     Dimension old_width, old_height;
  375.     char *unlink_filename;
  376.     XFontStruct *font;
  377.     Dimension borderWidth;
  378.     Position topMargin, bottomMargin, leftMargin, rightMargin;
  379.     Widget warp_button_widget = NULL;
  380.     int default_button_index = -1;
  381.     int warp_button_index = -1;
  382.  
  383.     npairs = parse_name_and_exit_code_list (button_list, &br);
  384.  
  385.     form = XtCreateManagedWidget ("form", formWidgetClass, parent, NULL, 0);
  386.     
  387.     n = 0;
  388.     XtSetArg (args[n], XtNleft, XtChainLeft); n++;
  389.     XtSetArg (args[n], XtNright, XtChainRight); n++;
  390.     XtSetArg (args[n], XtNtop, XtChainTop); n++;
  391.     XtSetArg (args[n], XtNbottom, XtChainBottom); n++;
  392.     XtSetArg (args[n], XtNresizable, TRUE); n++;
  393.  
  394.     if (filename) {
  395.     if (*filename == '-') unlink_filename = filename = copy_stdin ();
  396.     (void) get_file_dimensions(filename, &msg_columns, &msg_lines);
  397.     XtSetArg (args[n], XtNstring, filename); n++;
  398.     XtSetArg (args[n], XtNtype, XawAsciiFile); n++;
  399.     }
  400.     else {
  401.     if (!msgstr) msgstr = "xmessage called with no message string";
  402.     (void) get_message_dimensions(msgstr, &msg_columns, &msg_lines);
  403.     XtSetArg (args[n], XtNstring, msgstr); n++;
  404.     XtSetArg (args[n], XtNtype, XawAsciiString); n++;
  405.     }
  406.     XtSetArg (args[n], XtNscrollHorizontal, XawtextScrollWhenNeeded); n++;
  407.     XtSetArg (args[n], XtNscrollVertical, XawtextScrollWhenNeeded); n++;
  408.  
  409.     text = XtCreateManagedWidget ("text", asciiTextWidgetClass, form, args, n);
  410.  
  411.     /*
  412.      * Interrogate the text widget to find out which font it is using,
  413.      * so we can determine the dimensions of its character cell in order
  414.      * to resize the text widget.
  415.      *
  416.      * Note that if the user has explicitly specified a geometry then
  417.      * this resizing is for naught.
  418.      */
  419.  
  420.     n = 0;
  421.     XtSetArg (args[n], XtNborderWidth, &borderWidth);  n++;
  422.     XtSetArg (args[n], XtNtopMargin, &topMargin);  n++;
  423.     XtSetArg (args[n], XtNbottomMargin, &bottomMargin);  n++;
  424.     XtSetArg (args[n], XtNleftMargin, &leftMargin);  n++;
  425.     XtSetArg (args[n], XtNrightMargin, &rightMargin);  n++;
  426.     XtSetArg (args[n], XtNwidth, &old_width);  n++;
  427.     XtSetArg (args[n], XtNheight, &old_height);  n++;
  428.     XtSetArg (args[n], XtNfont, &font);  n++;
  429.  
  430.     XtGetValues(text, args, n);
  431.  
  432.     font_width = font->max_bounds.width;
  433.     font_height = font->ascent + font->descent;
  434.  
  435.     width = 0;
  436.     height = 0;
  437.  
  438.     if (columns == 0) {        /* no explicit columns on command line */
  439.     columns = msg_columns;    /* use actual columns in the message */
  440.     if (columns < min_columns) columns = min_columns;
  441.     if (columns > max_columns) {
  442.         columns = max_columns;
  443.         height += SCROLLBAR_HEIGHT;
  444.     }
  445.     }
  446.     width += columns * font_width;
  447.     width += borderWidth + leftMargin + rightMargin + borderWidth;
  448.  
  449.     if (lines == 0) {        /* no explicit lines on command line */
  450.     lines = msg_lines;    /* use actual lines in the message */
  451.     if (lines < min_lines) lines = min_lines;
  452.     if (lines > max_lines) {
  453.         lines = max_lines;
  454.         width += SCROLLBAR_WIDTH;
  455.     }
  456.     }
  457.     height += lines * font_height;
  458.     height += borderWidth + topMargin + bottomMargin + borderWidth;
  459.  
  460.     if (width < old_width) width = old_width;
  461.     if (height < old_height) height = old_height;
  462.  
  463.     n = 0;
  464.     XtSetArg (args[n], XtNwidth, (Dimension) width); n++;
  465.     XtSetArg (args[n], XtNheight, (Dimension) height); n++;
  466.  
  467.     XtSetValues(text, args, n);
  468.  
  469.     /*
  470.      * Create the buttons
  471.      */
  472.     n = 0;
  473.     XtSetArg (args[n], XtNleft, XtChainLeft); n++;
  474.     XtSetArg (args[n], XtNright, XtChainLeft); n++;
  475.     XtSetArg (args[n], XtNtop, XtChainBottom); n++;
  476.     XtSetArg (args[n], XtNbottom, XtChainBottom); n++;
  477.     XtSetArg (args[n], XtNfromVert, text); n++;
  478.     XtSetArg (args[n], XtNvertDistance, 5); n++;
  479.  
  480.     prev = NULL;
  481.     for (i = 0; i < npairs; i++) {
  482.  
  483.     thisn = n;
  484.     XtSetArg (args[thisn], XtNfromHoriz, prev); thisn++;
  485.     prev = XtCreateManagedWidget (br[i].name, commandWidgetClass,
  486.                       form, args, thisn);
  487.     br[i].widget = prev;
  488.     br[i].print_value = print_value;
  489.     br[i].filename = unlink_filename;
  490.     XtAddCallback (prev, XtNcallback, handle_button, (XtPointer) &br[i]);
  491.  
  492.     if (default_button && !strcmp(default_button, br[i].name))
  493.         default_button_index = i;
  494.  
  495.     if (warp_button && !strcmp(warp_button, br[i].name))
  496.         warp_button_index = i;
  497.     }
  498.  
  499.     /*
  500.      * if we didn't find a warp button, possibly the default button
  501.      * is also the warp button
  502.      */
  503.     if (warp_button_index < 0 && default_button_index >= 0 &&
  504.     warp_button != NULL && !strcmp(warp_button,"default")) {
  505.     warp_button_index = default_button_index;
  506.     }
  507.  
  508.     /*
  509.      * if there is a default button, double its border width, then adjust
  510.      * the vertical distance of all other buttons to maintain alignment
  511.      * with the default button
  512.      */
  513.     if (default_button_index >= 0) {
  514.     Dimension old_border, new_border;
  515.     default_button_widget = br[default_button_index].widget;
  516.     XtVaGetValues(default_button_widget, XtNborderWidth, &old_border, NULL);
  517.     new_border = old_border * 2;
  518.     XtVaSetValues(default_button_widget, XtNborderWidth, new_border, NULL);
  519.  
  520.     for (i = 0; i < npairs; i++) {
  521.         if (i != default_button_index) {
  522.         int vertdistance;
  523.         XtVaGetValues(br[i].widget, XtNvertDistance, &vertdistance, NULL);
  524.         vertdistance += old_border;
  525.         XtVaSetValues(br[i].widget, XtNvertDistance, vertdistance, NULL);
  526.         }
  527.     }
  528.     }
  529.     else if (default_button != NULL && *default_button != '\0') {
  530.     fprintf(stderr, "%s:  no such default button '%s'\n",
  531.             ProgramName, default_button);
  532.     }
  533.  
  534.     /*
  535.      * if there is a warp button, set up an exposure handler to do the warp
  536.      */
  537.     if (warp_button_index >= 0) {
  538.     warp_button_widget = br[warp_button_index].widget;
  539.     XtAddEventHandler(warp_button_widget, ExposureMask, False,
  540.     (XtEventHandler)warp_to_button, (XtPointer) &br[warp_button_index]);
  541.     }
  542.     else if (warp_button != NULL && *warp_button != '\0') {
  543.     fprintf(stderr, "%s:  no such warp button '%s'\n",
  544.             ProgramName, warp_button);
  545.     }
  546.  
  547.     return form;
  548. }
  549.